package prgechecsimpla;

import java.util.*;
import prgechecsmodel.IBoard;
import prgechecsmodel.IDefinition;
import prgechecsmodel.IMove;

final class HistoryData {

    Move m;
    int capture;
    int castle;
    int ep;
    int fifty;
}

final public class Board implements IBoard, IDefinition {
    // Side to move
    // Represents the 0x88 board

    public int[] boardArray;
    public int[] boardArrayUnique; // Keeps track of what index a piece on a
    // certain square has in the corresponding
    // piece list
    public int toMove; // Side to move on the board
    public int enPassant; // Index on boardArray where en passant is
    // available, -1 if none
    public int white_castle; // White's ability to castle
    public int black_castle; // Black's ability to castle
    public int movesFifty; // Keeps track of half-moves since last capture
    // or pawn move
    public int movesFull; // Keeps track of total full moves in the game
    public int[] history; // Keeps track of previous positions for unmake move
    public int[] captureHistory;
    public long[] zobristHistory; // Same as history but keeps tracks of
    // passed zobrist keys
    public long[] pawnZobristHistory;
    public int historyIndex; // Keeps track of number of takebacks in the
    // history arrays
    public long zobristKey; // The zobrist key for the position
    public long pawnZobristKey; // The zobrist key for the pawns
    public PieceList w_pawns;
    public PieceList b_pawns;
    public PieceList w_knights;
    public PieceList b_knights;
    public PieceList w_bishops;
    public PieceList b_bishops;
    public PieceList w_rooks;
    public PieceList b_rooks;
    public PieceList w_queens;
    public PieceList b_queens;
    public PieceList w_king;
    public PieceList b_king;
    int side = LIGHT;
    int xside = DARK;
    int castle = 15;
    int ep = -1;
    int fifty = 0;
    int hply = 0;
//    int history[][] = new int[ 64 ][ 64 ];
    HistoryData histDat[] = new HistoryData[ HIST_STACK ];

    public class PieceList {

        public int[] pieces;
        public int count;

        public PieceList() {
            this.pieces = new int[ 10 ];
            this.count = 0;
        }

        public void addPiece(int boardIndex) {
            boardArrayUnique[boardIndex] = count;
            pieces[count] = boardIndex;
            count++;
        }
    }

    public Board() {
        this.boardArray = new int[ 128 ];
        this.boardArrayUnique = new int[ 128 ];
        this.toMove = 1; // White to move
        this.enPassant = -1; // No en passants available
        this.white_castle = CASTLE_NONE;
        this.black_castle = CASTLE_NONE;
        this.w_pawns = new PieceList();
        this.b_pawns = new PieceList();
        this.w_knights = new PieceList();
        this.b_knights = new PieceList();
        this.w_bishops = new PieceList();
        this.b_bishops = new PieceList();
        this.w_rooks = new PieceList();
        this.b_rooks = new PieceList();
        this.w_queens = new PieceList();
        this.b_queens = new PieceList();
        this.w_king = new PieceList();
        this.b_king = new PieceList();
        this.history = new int[ 4096 ];
        this.captureHistory = new int[ 4096 ];
        this.zobristHistory = new long[ 4096 ];
        this.pawnZobristHistory = new long[ 4096 ];
        this.zobristKey = 0;
        this.pawnZobristKey = 0;
    }

    @Override
    public int getColor(int i, int j) {
        return color[(i << 3) + j];
    }

    @Override
    public int getPiece(int i, int j) {
        return piece[(i << 3) + j];
    }

    @Override
    public boolean isWhiteToMove() {
        return (side == LIGHT);
    }

    /* inCheck() returns true if side s is in check and false
    otherwise. It just scans the board to find side s's king
    and calls attack() to see if it's being attacked. */
    @Override
    public boolean inCheck(int s) {
        int i;

        for (i = 0 ; i < 64 ; ++i) {
            if (piece[i] == KING && color[i] == s) {
                return attack(i, s ^ 1);
            }
        }
        return true;  /* shouldn't get here */
    }


    /* attack() returns true if square sq is being attacked by side
    s and false otherwise. */
    boolean attack(int sq, int s) {
        int i, j, n;

        for (i = 0 ; i < 64 ; ++i) {
            if (color[i] == s) {
                int p = piece[i];
                if (p == PAWN) {
                    if (s == LIGHT) {
                        if (COL(i) != 0 && i - 9 == sq) {
                            return true;
                        }
                        if (COL(i) != 7 && i - 7 == sq) {
                            return true;
                        }
                    } else {
                        if (COL(i) != 0 && i + 7 == sq) {
                            return true;
                        }
                        if (COL(i) != 7 && i + 9 == sq) {
                            return true;
                        }
                    }
                } else {
                    for (j = 0 ; j < offsets[p] ; ++j) {
                        for (n = i ;;) {
                            n = mailbox[mailbox64[n] + offset[p][j]];
                            if (n == -1) {
                                break;
                            }
                            if (n == sq) {
                                return true;
                            }
                            if (color[n] != EMPTY) {
                                break;
                            }
                            if (!slide[p]) {
                                break;
                            }
                        }
                    }
                }
            }
        }
        return false;
    }


    /* gen() generates pseudo-legal moves for the current position.
    It scans the board to find friendly pieces and then determines
    what squares they attack. When it finds a piece/square
    combination, it calls genPush to put the move on the "move
    stack." */
    @Override
    public TreeSet<IMove> gen() {
        TreeSet<IMove> ret = new TreeSet<IMove>();

        for (int i = 0 ; i < 64 ; ++i) {
            if (color[i] == side) {
                if (piece[i] == PAWN) {
                    if (side == LIGHT) {
                        if (COL(i) != 0 && color[i - 9] == DARK) {
                            genPush(ret, i, i - 9, 17);
                        }
                        if (COL(i) != 7 && color[i - 7] == DARK) {
                            genPush(ret, i, i - 7, 17);
                        }
                        if (color[i - 8] == EMPTY) {
                            genPush(ret, i, i - 8, 16);
                            if (i >= 48 && color[i - 16] == EMPTY) {
                                genPush(ret, i, i - 16, 24);
                            }
                        }
                    } else {
                        if (COL(i) != 0 && color[i + 7] == LIGHT) {
                            genPush(ret, i, i + 7, 17);
                        }
                        if (COL(i) != 7 && color[i + 9] == LIGHT) {
                            genPush(ret, i, i + 9, 17);
                        }
                        if (color[i + 8] == EMPTY) {
                            genPush(ret, i, i + 8, 16);
                            if (i <= 15 && color[i + 16] == EMPTY) {
                                genPush(ret, i, i + 16, 24);
                            }
                        }
                    }
                } else {
                    for (int j = 0 ; j < offsets[piece[i]] ; ++j) {
                        for (int n = i ;;) {
                            n = mailbox[mailbox64[n] + offset[piece[i]][j]];
                            if (n == -1) {
                                break;
                            }
                            if (color[n] != EMPTY) {
                                if (color[n] == xside) {
                                    genPush(ret, i, n, 1);
                                }
                                break;
                            }
                            genPush(ret, i, n, 0);
                            if (!slide[piece[i]]) {
                                break;
                            }
                        }
                    }
                }
            }
        }

        /* generate castle moves */
        if (side == LIGHT) {
            if ((castle & 1) != 0) {
                genPush(ret, E1, G1, 2);
            }
            if ((castle & 2) != 0) {
                genPush(ret, E1, C1, 2);
            }
        } else {
            if ((castle & 4) != 0) {
                genPush(ret, E8, G8, 2);
            }
            if ((castle & 8) != 0) {
                genPush(ret, E8, C8, 2);
            }
        }

        /* generate en passant moves */
        if (ep != -1) {
            if (side == LIGHT) {
                if (COL(ep) != 0 && color[ep + 7] == LIGHT && piece[ep + 7] == PAWN) {
                    genPush(ret, ep + 7, ep, 21);
                }
                if (COL(ep) != 7 && color[ep + 9] == LIGHT && piece[ep + 9] == PAWN) {
                    genPush(ret, ep + 9, ep, 21);
                }
            } else {
                if (COL(ep) != 0 && color[ep - 9] == DARK && piece[ep - 9] == PAWN) {
                    genPush(ret, ep - 9, ep, 21);
                }
                if (COL(ep) != 7 && color[ep - 7] == DARK && piece[ep - 7] == PAWN) {
                    genPush(ret, ep - 7, ep, 21);
                }
            }
        }
        return ret;
    }


    /* genCaps() is basically a copy of gen() that's modified to
    only generate capture and promote moves. It's used by the
    quiescence search. */
    @Override
    public TreeSet<IMove> genCaps() {
        TreeSet<IMove> ret = new TreeSet<IMove>();

        for (int i = 0 ; i < 64 ; ++i) {
            if (color[i] == side) {
                if (piece[i] == PAWN) {
                    if (side == LIGHT) {
                        if (COL(i) != 0 && color[i - 9] == DARK) {
                            genPush(ret, i, i - 9, 17);
                        }
                        if (COL(i) != 7 && color[i - 7] == DARK) {
                            genPush(ret, i, i - 7, 17);
                        }
                        if (i <= 15 && color[i - 8] == EMPTY) {
                            genPush(ret, i, i - 8, 16);
                        }
                    }
                    if (side == DARK) {
                        if (COL(i) != 0 && color[i + 7] == LIGHT) {
                            genPush(ret, i, i + 7, 17);
                        }
                        if (COL(i) != 7 && color[i + 9] == LIGHT) {
                            genPush(ret, i, i + 9, 17);
                        }
                        if (i >= 48 && color[i + 8] == EMPTY) {
                            genPush(ret, i, i + 8, 16);
                        }
                    }
                } else {
                    for (int j = 0 ; j < offsets[piece[i]] ; ++j) {
                        for (int n = i ;;) {
                            n = mailbox[mailbox64[n] + offset[piece[i]][j]];
                            if (n == -1) {
                                break;
                            }
                            if (color[n] != EMPTY) {
                                if (color[n] == xside) {
                                    genPush(ret, i, n, 1);
                                }
                                break;
                            }
                            if (!slide[piece[i]]) {
                                break;
                            }
                        }
                    }
                }
            }
        }
        if (ep != -1) {
            if (side == LIGHT) {
                if (COL(ep) != 0 && color[ep + 7] == LIGHT && piece[ep + 7] == PAWN) {
                    genPush(ret, ep + 7, ep, 21);
                }
                if (COL(ep) != 7 && color[ep + 9] == LIGHT && piece[ep + 9] == PAWN) {
                    genPush(ret, ep + 9, ep, 21);
                }
            } else {
                if (COL(ep) != 0 && color[ep - 9] == DARK && piece[ep - 9] == PAWN) {
                    genPush(ret, ep - 9, ep, 21);
                }
                if (COL(ep) != 7 && color[ep - 7] == DARK && piece[ep - 7] == PAWN) {
                    genPush(ret, ep - 7, ep, 21);
                }
            }
        }
        return ret;
    }

    /* genPush() puts a move on the move stack, unless it's a
    pawn promotion that needs to be handled by genPromote().
    It also assigns a score to the move for alpha-beta move
    ordering. If the move is a capture, it uses MVV/LVA
    (Most Valuable Victim/Least Valuable Attacker). Otherwise,
    it uses the move's history heuristic value. Note that
    1,000,000 is added to a capture move's score, so it
    always gets ordered above a "normal" move. */
    @Override
    public void genPush(TreeSet<IMove> ret, int from, int to, int bits) {
        if ((bits & 16) != 0) {
            if (side == LIGHT) {
                if (to <= H8) {
                    genPromote(ret, from, to, bits);
                    return;
                }
            } else {
                if (to >= A1) {
                    genPromote(ret, from, to, bits);
                    return;
                }
            }
        }

        IMove g = new Move(from, to, 0, bits);

//        if (color[to] != EMPTY) {
//            g.setScore(1000000 + (piece[to] * 10) - piece[from]);
//        } else {
//            g.setScore(history[from][to]);
//        }
        ret.add(g);
    }


    /* genPromote() is just like genPush(), only it puts 4 moves
    on the move stack, one for each possible promotion piece */
    @Override
    public void genPromote(TreeSet<IMove> ret, int from, int to, int bits) {
        for (char i = KNIGHT ; i <= QUEEN ; ++i) {
            Move g = new Move(from, to, i, (bits | 32));
            g.setScore(1000000 + (i * 10));
            ret.add(g);
        }
    }

    /* makemove() makes a move. If the move is illegal, it
    undoes whatever it did and returns false. Otherwise, it
    returns true. */
    boolean makeMove(Move m) {

        /* test to see if a castle move is legal and move the rook
        (the king is moved with the usual move code later) */
        if ((m.bits & 2) != 0) {
            int from, to;

            if (inCheck(side)) {
                return false;
            }
            switch (m.to) {
                case 62:
                    if (color[F1] != EMPTY || color[G1] != EMPTY ||
                            attack(F1, xside) || attack(G1, xside)) {
                        return false;
                    }
                    from = H1;
                    to = F1;
                    break;
                case 58:
                    if (color[B1] != EMPTY || color[C1] != EMPTY || color[D1] != EMPTY ||
                            attack(C1, xside) || attack(D1, xside)) {
                        return false;
                    }
                    from = A1;
                    to = D1;
                    break;
                case 6:
                    if (color[F8] != EMPTY || color[G8] != EMPTY ||
                            attack(F8, xside) || attack(G8, xside)) {
                        return false;
                    }
                    from = H8;
                    to = F8;
                    break;
                case 2:
                    if (color[B8] != EMPTY || color[C8] != EMPTY || color[D8] != EMPTY ||
                            attack(C8, xside) || attack(D8, xside)) {
                        return false;
                    }
                    from = A8;
                    to = D8;
                    break;
                default:  /* shouldn't get here */
                    from = -1;
                    to = -1;
                    break;
            }
            color[to] = color[from];
            piece[to] = piece[from];
            color[from] = EMPTY;
            piece[from] = EMPTY;
        }

        /* back up information so we can take the move back later. */
        histDat[hply] = new HistoryData();
        histDat[hply].m = m;
        histDat[hply].capture = piece[m.to];
        histDat[hply].castle = castle;
        histDat[hply].ep = ep;
        histDat[hply].fifty = fifty;
        ++hply;

        /* update the castle, en passant, and
        fifty-move-draw variables */
        castle &= castleMask[m.from] & castleMask[m.to];
        if ((m.bits & 8) != 0) {
            if (side == LIGHT) {
                ep = m.to + 8;
            } else {
                ep = m.to - 8;
            }
        } else {
            ep = -1;
        }
        if ((m.bits & 17) != 0) {
            fifty = 0;
        } else {
            ++fifty;
        }

        /* move the piece */
        color[m.to] = side;
        if ((m.bits & 32) != 0) {
            piece[m.to] = m.promote;
        } else {
            piece[m.to] = piece[m.from];
        }
        color[m.from] = EMPTY;
        piece[m.from] = EMPTY;

        /* erase the pawn if this is an en passant move */
        if ((m.bits & 4) != 0) {
            if (side == LIGHT) {
                color[m.to + 8] = EMPTY;
                piece[m.to + 8] = EMPTY;
            } else {
                color[m.to - 8] = EMPTY;
                piece[m.to - 8] = EMPTY;
            }
        }

        /* switch sides and test for legality (if we can capture
        the other guy's king, it's an illegal position and
        we need to take the move back) */
        side ^= 1;
        xside ^= 1;
        if (inCheck(xside)) {
            takeBack();
            return false;
        }
        return true;
    }


    /* takeBack() is very similar to makeMove(), only backwards :)  */
    @Override
    public void takeBack() {
        side ^= 1;
        xside ^= 1;
        --hply;
        Move m = histDat[hply].m;
        castle = histDat[hply].castle;
        ep = histDat[hply].ep;
        fifty = histDat[hply].fifty;
        color[m.from] = side;
        if ((m.bits & 32) != 0) {
            piece[m.from] = PAWN;
        } else {
            piece[m.from] = piece[m.to];
        }
        if (histDat[hply].capture == EMPTY) {
            color[m.to] = EMPTY;
            piece[m.to] = EMPTY;
        } else {
            color[m.to] = xside;
            piece[m.to] = histDat[hply].capture;
        }
        if ((m.bits & 2) != 0) {
            int from, to;

            switch (m.to) {
                case 62:
                    from = F1;
                    to = H1;
                    break;
                case 58:
                    from = D1;
                    to = A1;
                    break;
                case 6:
                    from = F8;
                    to = H8;
                    break;
                case 2:
                    from = D8;
                    to = A8;
                    break;
                default:  /* shouldn't get here */
                    from = -1;
                    to = -1;
                    break;
            }
            color[to] = side;
            piece[to] = ROOK;
            color[from] = EMPTY;
            piece[from] = EMPTY;
        }
        if ((m.bits & 4) != 0) {
            if (side == LIGHT) {
                color[m.to + 8] = xside;
                piece[m.to + 8] = PAWN;
            } else {
                color[m.to - 8] = xside;
                piece[m.to - 8] = PAWN;
            }
        }
    }

    @Override
    public String toString() {
        int i;

        StringBuffer sb = new StringBuffer("\n8 ");
        for (i = 0 ; i < 64 ; ++i) {
            switch (color[i]) {
                case EMPTY:
                    sb.append(" .");
                    break;
                case LIGHT:
                    sb.append(" ");
                    sb.append(pieceChar[piece[i]]);
                    break;
                case DARK:
                    sb.append(" ");
                    sb.append((char) (pieceChar[piece[i]] + ('a' - 'A')));
                    break;
                default:
                    throw new IllegalStateException("Square not EMPTY, LIGHT or DARK: " + i);
            }
            if ((i + 1) % 8 == 0 && i != 63) {
                sb.append("\n");
                sb.append(Integer.toString(7 - ROW(i)));
                sb.append(" ");
            }
        }
        sb.append("\n\n   a b c d e f g h\n\n");
        return sb.toString();
    }

    /* reps() returns the number of times that the current
    position has been repeated. Thanks to John Stanback
    for this clever algorithm. */
    @Override
    public int reps() {
        int b[] = new int[ 64 ];
        int c = 0;  /* count of squares that are different from
        the current position */
        int r = 0;  /* number of repetitions */

        /* is a repetition impossible? */
        if (fifty <= 3) {
            return 0;
        }

        /* loop through the reversible moves */
        for (int i = hply - 1 ; i >= hply - fifty - 1 ; --i) {
            if (++b[histDat[i].m.from] == 0) {
                --c;
            } else {
                ++c;
            }
            if (--b[histDat[i].m.to] == 0) {
                --c;
            } else {
                ++c;
            }
            if (c == 0) {
                ++r;
            }
        }

        return r;
    }

    static int COL(int x) {
        return (x & 7);
    }

    static int ROW(int x) {
        return (x >> 3);
    }

    @Override
    public int distance(int squareA, int squareB) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public int gen_allLegalMoves(TreeSet<IMove> moves, int startIndex) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public int gen_allLegalMoves(int[] moves, int i) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public boolean validate(int move) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public boolean isAttacked(int attacked, int side) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public boolean makeMove(IMove m) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void makeMove(int i) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public int rank(int index) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public int col(int x) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void setupStart() {
    }

    @Override
    public void unmakeMove(int move) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public String getFen() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    /**
     * Takes a FEN-string and sets the board accordingly
     *
     * @param String
     *            fen
     */
    @Override
    public final void inputFen(String fen) {
//        historyIndex = 0; // Reset to make sure we start from the beginning
        String trimmedFen = fen.trim(); // Removes any white spaces in front or
        // behind the string
        boardArray = new int[ 128 ]; // Empties the board from any pieces

        for (int i = 0 ; i < 128 ; i++) {
            boardArrayUnique[i] = -1;
        }
        // Reset the piece lists
        this.w_pawns = new PieceList();
        this.b_pawns = new PieceList();
        this.w_knights = new PieceList();
        this.b_knights = new PieceList();
        this.w_bishops = new PieceList();
        this.b_bishops = new PieceList();
        this.w_rooks = new PieceList();
        this.b_rooks = new PieceList();
        this.w_queens = new PieceList();
        this.b_queens = new PieceList();
        this.w_king = new PieceList();
        this.b_king = new PieceList();

        String currentChar; // Holds the current character in the fen

        int i = 0; // Used to go through the fen-string character by character

        int boardIndex = 112; // Keeps track of current index on the board
        // (while adding pieces)
        // Starts at "a8" (index 112) since the fen string starts on this square

        int currentStep = 0; // This will be incremented when a space is
        // detected in the string
        // 0 - Pieces
        // 1 - Side to move
        // 2 - Castling rights
        // 3 - En passant square
        // 4 - Half-moves (for 50 move rule) and full moves

        white_castle = CASTLE_NONE; // Resetting, will be changed below if
        // castling rights are found
        black_castle = CASTLE_NONE;
        boolean fenFinished = false; // Set to true when we're at the end of
        // the fen-string
        while (!fenFinished && i < trimmedFen.length()) {
            currentChar = trimmedFen.substring(i, i + 1); // Gets the current
            // character from
            // the fen-string

            // If a space is detected, get the next character, and move to next
            // step
            if (" ".equals(currentChar)) {
                i++;
                currentChar = trimmedFen.substring(i, i + 1);
                currentStep++;
            }

            switch (currentStep) // Determine what step we're on
            {
                case 0: // Pieces
                {
                    switch (currentChar.charAt(0)) // See what piece is on the
                    // square
                    {
                        // If character is a '/' move to first row on next rank
                        case '/':
                            boardIndex -= 24;
                            break;

                        // If the character is a piece, add it and move to next square
                        case 'K':
                            boardArray[boardIndex] = W_KING;
                            w_king.addPiece(boardIndex);
                            boardIndex++;
                            break;
                        case 'Q':
                            boardArray[boardIndex] = W_QUEEN;
                            w_queens.addPiece(boardIndex);
                            boardIndex++;
                            break;
                        case 'R':
                            boardArray[boardIndex] = W_ROOK;
                            w_rooks.addPiece(boardIndex);
                            boardIndex++;
                            break;
                        case 'B':
                            boardArray[boardIndex] = W_BISHOP;
                            w_bishops.addPiece(boardIndex);
                            boardIndex++;
                            break;
                        case 'N':
                            boardArray[boardIndex] = W_KNIGHT;
                            w_knights.addPiece(boardIndex);
                            boardIndex++;
                            break;
                        case 'P':
                            boardArray[boardIndex] = W_PAWN;
                            w_pawns.addPiece(boardIndex);
                            boardIndex++;
                            break;
                        case 'k':
                            boardArray[boardIndex] = B_KING;
                            b_king.addPiece(boardIndex);
                            boardIndex++;
                            break;
                        case 'q':
                            boardArray[boardIndex] = B_QUEEN;
                            b_queens.addPiece(boardIndex);
                            boardIndex++;
                            break;
                        case 'r':
                            boardArray[boardIndex] = B_ROOK;
                            b_rooks.addPiece(boardIndex);
                            boardIndex++;
                            break;
                        case 'b':
                            boardArray[boardIndex] = B_BISHOP;
                            b_bishops.addPiece(boardIndex);
                            boardIndex++;
                            break;
                        case 'n':
                            boardArray[boardIndex] = B_KNIGHT;
                            b_knights.addPiece(boardIndex);
                            boardIndex++;
                            break;
                        case 'p':
                            boardArray[boardIndex] = B_PAWN;
                            b_pawns.addPiece(boardIndex);
                            boardIndex++;
                            break;

                        // If no piece was found, it has to be a number of empty squares
                        // so move to that board index
                        default:
                            boardIndex += Integer.parseInt(currentChar);
                    }
                    break;
                }
                case 1: // Side to move
                {
                    if ("w".equals(currentChar)) {
                        toMove = WHITE_TO_MOVE;
                    } else {
                        toMove = BLACK_TO_MOVE;
                    }
                    break;
                }
                case 2: // Castling rights
                {
                    // '-' states that no castling is available so we simply keep
                    // the values
                    // we set before the while-loop, and don't need to do anything
                    // here.
                    switch (currentChar.charAt(0)) {
                        // White can atleast castle short
                        case 'K':
                            white_castle = CASTLE_SHORT;
                            break;

                        case 'Q': // White can atleast castle long
                        {
                            // If white already can castle short, do both, else only
                            // long
                            if (white_castle == CASTLE_SHORT) {
                                white_castle = CASTLE_BOTH;
                            } else {
                                white_castle = CASTLE_LONG;
                            }
                            break;
                        }
                        // Black can atleast castle short
                        case 'k':
                            black_castle = CASTLE_SHORT;
                            break;

                        case 'q': // Black can atleast castle long
                        {
                            // If black already can castle short, do both, else only
                            // long
                            if (black_castle == CASTLE_SHORT) {
                                black_castle = CASTLE_BOTH;
                            } else {
                                black_castle = CASTLE_LONG;
                            }
                            break;
                        }
                    }
                    break;
                }
                case 3: // En passant
                {
                    if ("-".equals(currentChar)) {
                        enPassant = -1;
                    } else {
                        switch (currentChar.charAt(0)) // Find the row
                        {
                            case 'a':
                                enPassant = 0;
                                break;
                            case 'b':
                                enPassant = 1;
                                break;
                            case 'c':
                                enPassant = 2;
                                break;
                            case 'd':
                                enPassant = 3;
                                break;
                            case 'e':
                                enPassant = 4;
                                break;
                            case 'f':
                                enPassant = 5;
                                break;
                            case 'g':
                                enPassant = 6;
                                break;
                            case 'h':
                                enPassant = 7;
                                break;
                        }
                        // Get the next character (the rank)
                        i++;
                        currentChar = trimmedFen.substring(i, i + 1);

                        // On rank 3 or else rank 6
                        if ("3".equals(currentChar)) {
                            enPassant += 32; // Add 2 ranks to index
                        } else {
                            enPassant += 80; // Add 5 ranks to index
                        }
                    }
                    break;
                }
                case 4: // Half-moves (50 move rule) and full moves
                {
                    // If the next character is a space, we're done with half-moves
                    // and
                    // can insert them
                    if (" ".equals(trimmedFen.substring(i + 1, i + 2))) {
                        movesFifty = Integer.parseInt(currentChar);
                    } // If the next character is not a space, we know it's a number
                    // and since half-moves can't be higher than 50 (or it can, but
                    // the game
                    // is drawn so there's not much point to it), we can assume
                    // there are two numbers and then we're done with half-moves.
                    else {
                        movesFifty = Integer.parseInt(trimmedFen.substring(i, i + 2));
                        i++;
                    }
                    i += 2;
                    movesFull = Integer.parseInt(trimmedFen.substring(i));
                    fenFinished = true; // We're done with the fen-string and can
                    // exit the loop
                    break;
                }
            }
            i++; // Move to the next character in the fen-string
        }
//        zobristKey = Zobrist.getZobristKey(this); // The board is now setup so
//        // we can get the inital
//        // zobrist key
//        pawnZobristKey = Zobrist.getPawnZobristKey(this);
    }
}
